package com.old.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Service
@Slf4j
public class BigModelService {

    // 接口常量配置
    private static final String hostUrl = "https://spark-api.xf-yun.com/v1.1/chat";
    private static final String appid = "7097491e";
    private static final String apiSecret = "MGIwMzkxMDRiZTgwYjFjMDQyYWNhMjMz";
    private static final String apiKey = "1a5638dd794eaae426938a33ade1c894";
    private static final String domain = "lite";

    // 为每个用户维护独立会话（历史记录、累计回答、是否正在处理中）
    private Map<String, UserSession> userSessions = new ConcurrentHashMap<>();
    private static final Gson gson = new Gson();

    // 用户会话类
    private static class UserSession {
        List<RoleContent> historyList = new ArrayList<>();
        String totalAnswer = "";
        Boolean isProcessing = false;
    }

    /**
     * 启动大模型对话，通过 SSE 将结果流式返回给前端
     * 修改后的 start 方法增加了 userId 参数，用于区分不同用户
     *
     * @param userId  用户标识
     * @param question 用户输入的问题
     * @param emitter  SSE 推送对象
     * @throws Exception 异常
     */
    public void start(String question, SseEmitter emitter,String userId ) throws Exception {
        UserSession session = userSessions.computeIfAbsent(userId, key -> new UserSession());
        if (!session.isProcessing) {
            session.isProcessing = true;
            // 清空上一次累计回答
            session.totalAnswer = "";
            // 将用户输入的问题添加到会话历史中
            RoleContent roleContent = new RoleContent();
            roleContent.setRole("user");
            roleContent.setContent("假如你是养老平台的助手，名字是小超，我是用户，下面是我的问题，回答不要用markdown格式，直接用纯文本回答"+question);
            session.historyList.add(roleContent);

            String authUrl = getAuthUrl(hostUrl, apiKey, apiSecret);
            OkHttpClient client = new OkHttpClient.Builder().build();
            String url = authUrl.replace("http://", "ws://").replace("https://", "wss://");
            Request request = new Request.Builder().url(url).build();

            client.newWebSocket(request, new BigModelWebSocketListener(emitter, session));
        } else {
            // 如果当前用户正在处理请求，告知用户稍后再试
            try {
                emitter.send(SseEmitter.event().data("正在处理上一个回复，请稍后再试"));
            } catch (IOException e) {
                e.printStackTrace();
            }
            emitter.complete();
        }
    }

    /**
     * WebSocket 回调处理器，通过 SSE 将大模型返回的数据推送到前端
     */
    private class BigModelWebSocketListener extends WebSocketListener {

        private SseEmitter emitter;
        private Boolean wsCloseFlag = false;
        private UserSession session;

        public BigModelWebSocketListener(SseEmitter emitter, UserSession session) {
            this.emitter = emitter;
            this.session = session;
        }

        @Override
        public void onOpen(WebSocket webSocket, Response response) {
//            System.out.println("大模型连接已打开");
            // 启动发送请求的线程
            new MyThread(webSocket, session).start();
        }

        @Override
        public void onMessage(WebSocket webSocket, String text) {
            // 解析返回的 JSON 数据
            JsonParse myJsonParse = gson.fromJson(text, JsonParse.class);
            if (myJsonParse.header.code != 0) {
                log.error("发生错误，错误码为：{}" , myJsonParse.header.code);
                log.error("本次请求的sid为：{}" ,myJsonParse.header.sid);
                webSocket.close(1000, "");
                return;
            }
            List<Text> textList = myJsonParse.payload.choices.text;
            for (Text temp : textList) {
                System.out.println("模型回复：" + temp.content);
                try {
                    // 通过 SSE 推送返回的回复
                    emitter.send(SseEmitter.event().data(temp.content));
                } catch (IOException e) {
                    e.printStackTrace();
                }
                session.totalAnswer += temp.content;
            }
            // 当 status==2 表示回复结束
            if (myJsonParse.header.status == 2) {
                try {
                    emitter.send(SseEmitter.event().data("responseEnd"));
                    emitter.complete();
                } catch (IOException e) {
                    emitter.completeWithError(e);
                }
                // 将大模型的回复保存到当前用户的历史记录中
                RoleContent roleContent = new RoleContent();
                roleContent.setRole("assistant");
                roleContent.setContent(session.totalAnswer);
                session.historyList.add(roleContent);

                // 检查历史记录长度，超长则移除部分记录
                if (!canAddHistory(session.historyList)) {
                    for (int i = 0; i < 5 && !session.historyList.isEmpty(); i++) {
                        session.historyList.remove(0);
                    }
                }
                wsCloseFlag = true;
                session.isProcessing = false;
            }
        }

        @Override
        public void onFailure(WebSocket webSocket, Throwable t, Response response) {
            super.onFailure(webSocket, t, response);
            try {
                if (response != null) {
                    int code = response.code();
                    System.out.println("onFailure code:" + code);
                    System.out.println("onFailure body:" + response.body().string());
                    if (101 != code) {
                        System.out.println("connection failed");
                        System.exit(0);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        /**
         * 内部线程：构造请求参数并发送给大模型服务端
         */
        class MyThread extends Thread {
            private WebSocket webSocket;
            private UserSession session;

            public MyThread(WebSocket webSocket, UserSession session) {
                this.webSocket = webSocket;
                this.session = session;
            }

            @Override
            public void run() {
                try {
                    // 构造请求 JSON
                    JSONObject requestJson = new JSONObject();

                    // header
                    JSONObject header = new JSONObject();
                    header.put("app_id", appid);
                    header.put("uid", UUID.randomUUID().toString().substring(0, 10));

                    // parameter
                    JSONObject parameter = new JSONObject();
                    JSONObject chat = new JSONObject();
                    chat.put("domain", domain);
                    chat.put("temperature", 0.5);
                    chat.put("max_tokens", 4096);
                    parameter.put("chat", chat);

                    // payload
                    JSONObject payload = new JSONObject();
                    JSONObject message = new JSONObject();
                    JSONArray textArray = new JSONArray();

                    // 添加当前用户的历史对话记录
                    for (RoleContent tempRoleContent : session.historyList) {
                        textArray.add(JSON.toJSON(tempRoleContent));
                    }
                    message.put("text", textArray);
                    payload.put("message", message);

                    requestJson.put("header", header);
                    requestJson.put("parameter", parameter);
                    requestJson.put("payload", payload);

                    // 发送请求
                    webSocket.send(requestJson.toString());
                    // 等待大模型返回数据后关闭连接
                    while (true) {
                        Thread.sleep(200);
                        if (wsCloseFlag) {
                            break;
                        }
                    }
                    webSocket.close(1000, "");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 鉴权方法，生成带鉴权参数的 URL
     *
     * @param hostUrl   接口地址
     * @param apiKey    API Key
     * @param apiSecret API Secret
     * @return 带鉴权的 URL 字符串
     * @throws Exception 异常
     */
    public static String getAuthUrl(String hostUrl, String apiKey, String apiSecret) throws Exception {
        URL url = new URL(hostUrl);
        // GMT 时间格式
        SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
        format.setTimeZone(TimeZone.getTimeZone("GMT"));
        String date = format.format(new Date());
        // 拼接待签名字符串
        String preStr = "host: " + url.getHost() + "\n" +
                "date: " + date + "\n" +
                "GET " + url.getPath() + " HTTP/1.1";
        // SHA256 HMAC 加密
        Mac mac = Mac.getInstance("hmacsha256");
        SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), "hmacsha256");
        mac.init(spec);
        byte[] hexDigits = mac.doFinal(preStr.getBytes(StandardCharsets.UTF_8));
        String sha = Base64.getEncoder().encodeToString(hexDigits);
        // 拼接 authorization 参数
        String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"",
                apiKey, "hmac-sha256", "host date request-line", sha);
        // 构造最终 URL
        HttpUrl httpUrl = Objects.requireNonNull(HttpUrl.parse("https://" + url.getHost() + url.getPath()))
                .newBuilder()
                .addQueryParameter("authorization", Base64.getEncoder().encodeToString(authorization.getBytes(StandardCharsets.UTF_8)))
                .addQueryParameter("date", date)
                .addQueryParameter("host", url.getHost())
                .build();
        return httpUrl.toString();
    }

    /**
     * 判断历史记录是否超过设定长度
     *
     * @param historyList 当前用户的历史记录
     * @return true 表示长度未超过限制
     */
    public static boolean canAddHistory(List<RoleContent> historyList) {
        int historyLength = 0;
        for (RoleContent temp : historyList) {
            historyLength += temp.getContent().length();
        }
        return historyLength <= 12000;
    }

    // 内部类：用于解析大模型返回的 JSON 数据
    class JsonParse {
        Header header;
        Payload payload;
    }

    class Header {
        int code;
        int status;
        String sid;
    }

    class Payload {
        Choices choices;
    }

    class Choices {
        List<Text> text;
    }

    class Text {
        String role;
        String content;
    }

    class RoleContent {
        String role;
        String content;

        public String getRole() {
            return role;
        }

        public void setRole(String role) {
            this.role = role;
        }

        public String getContent() {
            return content;
        }

        public void setContent(String content) {
            this.content = content;
        }
    }
}
